package cn.jetpiece.cloud.joss.app.modular.object.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jetpiece.cloud.core.page.PageFactory;
import cn.jetpiece.cloud.joss.api.object.model.params.CreateBucketParam;
import cn.jetpiece.cloud.joss.api.object.model.params.UpdateBucketParam;
import cn.jetpiece.cloud.joss.api.object.model.query.BucketQuery;
import cn.jetpiece.cloud.joss.api.object.model.result.BucketResult;
import cn.jetpiece.cloud.joss.app.core.constant.OssConstants;
import cn.jetpiece.cloud.joss.app.modular.basics.entity.StorageClass;
import cn.jetpiece.cloud.joss.app.modular.basics.service.StorageClassService;
import cn.jetpiece.cloud.joss.app.modular.object.entity.Bucket;
import cn.jetpiece.cloud.joss.app.modular.object.entity.Provider;
import cn.jetpiece.cloud.joss.app.modular.object.entity.Region;
import cn.jetpiece.cloud.joss.app.modular.object.enums.BasicExceptionEnum;
import cn.jetpiece.cloud.joss.app.modular.object.mapper.BucketMapper;
import cn.jetpiece.cloud.joss.app.modular.object.model.BucketInfo;
import cn.jetpiece.cloud.joss.app.modular.object.service.BucketService;
import cn.jetpiece.cloud.joss.app.modular.object.service.ProviderService;
import cn.jetpiece.cloud.joss.app.modular.object.service.RegionService;
import cn.jetpiece.cloud.joss.app.modular.object.wrapper.BucketWrapper;
import cn.jetpiece.cloud.joss.app.modular.storage.factory.JossClientFactory;
import cn.jetpiece.cloud.model.exception.ServiceException;
import cn.jetpiece.cloud.model.page.PageResult;
import cn.jetpiece.cloud.model.util.SpringContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Date;

/**
 * <p>
 * 桶 服务实现类
 * </p>
 *
 * @author XuJZ
 * @since 2020-09-05
 */
@Service
public class BucketServiceImpl extends ServiceImpl<BucketMapper, Bucket> implements BucketService {
    private RedisTemplate<String, Object> redisTemplate;
    private StorageClassService storageClassService;

    @Resource
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setStorageClassService(StorageClassService storageClassService) {
        this.storageClassService = storageClassService;
    }

    @Override
    public boolean add(CreateBucketParam param) {
        LambdaQueryWrapper<Bucket> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Bucket::getBucketName, param.getBucketName());
        if (super.count(wrapper) > 0) {
            throw new ServiceException(BasicExceptionEnum.BUCKET_REPEAT);
        }
        Provider provider = SpringContextHolder.getBean(ProviderService.class).getById(param.getProviderId());
        if (provider == null) {
            throw new ServiceException(BasicExceptionEnum.PROVIDER_NOT_EXISTED);
        }
        Region region = SpringContextHolder.getBean(RegionService.class).getById(param.getRegionId());
        if (region == null) {
            throw new ServiceException(BasicExceptionEnum.REGION_NOT_EXISTED);
        } else if (!region.getProviderId().equals(param.getProviderId())) {
            throw new ServiceException(BasicExceptionEnum.REGION_NOT_EXISTED);
        }
        StorageClass storageClass = storageClassService.getById(param.getStorageClassId());
        if (storageClass == null) {
            throw new ServiceException(BasicExceptionEnum.STORAGE_CLASS_NOT_EXISTED);
        } else if (!storageClass.getProviderId().equals(param.getProviderId())) {
            throw new ServiceException(BasicExceptionEnum.STORAGE_CLASS_NOT_EXISTED);
        }
        Date date = new Date();
        Bucket bean = new Bucket();
        BeanUtil.copyProperties(param, bean);
        bean.setCreateTime(date);
        JossClientFactory.getJossClient(provider, region).createBucket(provider, region, bean, storageClass);
        return super.save(bean);
    }

    @Override
    public boolean update(UpdateBucketParam param) {
        Bucket oldBean = super.getById(param.getBucketName());
        if (oldBean == null) {
            throw new ServiceException(BasicExceptionEnum.BUCKET_NOT_EXISTED);
        }
        Bucket update = BeanUtil.copyProperties(param, Bucket.class);
        update.setBucketName(param.getBucketName());
        boolean result = super.updateById(update);
        if (result) {
            redisTemplate.boundHashOps(OssConstants.BUCKET).delete(oldBean.getBucketName());
        }
        redisTemplate.boundHashOps(OssConstants.BUCKET).put(oldBean.getBucketName(), update);
        return result;
    }

    @Override
    public Bucket getById(Serializable id) {
        if (ObjectUtil.isEmpty(id)) {
            return null;
        }
        Bucket bucket = (Bucket) redisTemplate.boundHashOps(OssConstants.BUCKET).get(id);
        if (bucket == null) {
            bucket = super.getById(id);
            if (bucket == null) {
                return null;
            }
            redisTemplate.boundHashOps(OssConstants.BUCKET).put(id, bucket);
        }
        return bucket;
    }

    @Override
    public boolean delete(String id) {
        Bucket bean = super.getById(id);
        if (bean == null) {
            throw new ServiceException(BasicExceptionEnum.BUCKET_NOT_EXISTED);
        }
        BucketInfo bucketInfo = getBucket(id);
        if (bucketInfo != null) {
            JossClientFactory.getJossClient(bucketInfo).deleteBucket(bucketInfo);
        }
        boolean result = super.removeById(id);
        if (result) {
            redisTemplate.boundHashOps(OssConstants.BUCKET).delete(id);
            redisTemplate.boundHashOps(OssConstants.BUCKET_INFO).delete(id);
        }
        return result;
    }

    @Override
    public PageResult<BucketResult> page(BucketQuery param) {
        LambdaQueryWrapper<Bucket> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotEmpty(param.getProviderId())) {
            wrapper.eq(Bucket::getProviderId, param.getProviderId());
        }
        if (StrUtil.isNotEmpty(param.getRegionId())) {
            wrapper.eq(Bucket::getRegionId, param.getRegionId());
        }
        if (StrUtil.isNotEmpty(param.getStorageClassId())) {
            wrapper.eq(Bucket::getStorageClassId, param.getStorageClassId());
        }
        if (StrUtil.isNotEmpty(param.getBucketName())) {
            wrapper.like(Bucket::getBucketName, param.getBucketName());
        }
        if (param.getDataRedundancyType() != null) {
            wrapper.eq(Bucket::getDataRedundancyType, param.getDataRedundancyType());
        }
        if (param.getAcl() != null) {
            wrapper.eq(Bucket::getAcl, param.getAcl());
        }
        IPage<Bucket> iPage = PageFactory.defaultPage();
        IPage<Bucket> page = super.page(iPage, wrapper);
        return new BucketWrapper(page).setWrapper(param.getWrapper()).wrapPageResult();
    }

    @Override
    public BucketInfo getBucket(String bucketName) {
        BucketInfo bucketInfo = (BucketInfo) redisTemplate.boundHashOps(OssConstants.BUCKET_INFO).get(bucketName);
        if (bucketInfo != null) {
            return bucketInfo;
        }
        Bucket bucket = this.getById(bucketName);
        if (bucket == null || bucket.getIsEnable().equals(0)) {
            throw new ServiceException(BasicExceptionEnum.BUCKET_NOT_EXISTED);
        }
        bucketInfo = new BucketInfo();
        BeanUtil.copyProperties(bucket, bucketInfo);
        Provider provider = SpringContextHolder.getBean(ProviderService.class).getById(bucket.getProviderId());
        if (provider == null) {
            throw new ServiceException(BasicExceptionEnum.PROVIDER_NOT_EXISTED);
        }
        bucketInfo.setProvider(provider);
        Region region = SpringContextHolder.getBean(RegionService.class).getById(bucket.getRegionId());
        if (region == null) {
            throw new ServiceException(BasicExceptionEnum.REGION_NOT_EXISTED);
        }
        bucketInfo.setRegion(region);
        redisTemplate.boundHashOps(OssConstants.BUCKET_INFO).put(bucketName, bucketInfo);
        return bucketInfo;
    }
}
